/*
 * jxController Project (2014)
 * 
 * by Denilson Edinaldo Pinto
 * 
 * LGPL
 * Copyright (C) 2014
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.jxController.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.sql.SQLException;
import java.sql.Time;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import javax.swing.text.JTextComponent;

import org.jxController.operator.FirstOneOperator;
import org.jxController.operator.LastOneOperator;
import org.jxController.view.component.OperatorComboBox;
import org.jxDao.dao.Dao;
import org.jxDao.dao.Persistable;
import org.jxDao.dao.filter.EqualFilter;
import org.jxDao.dao.filter.Filterable;
import org.jxDao.dao.filter.GreaterOrEqualFilter;
import org.jxDao.dao.filter.GreaterThanFilter;
import org.jxDao.dao.filter.ILikeBetweenFilter;
import org.jxDao.dao.filter.IsNotNullFilter;
import org.jxDao.dao.filter.IsNullFilter;
import org.jxDao.dao.filter.LessOrEqualFilter;
import org.jxDao.dao.filter.LessThanFilter;
import org.jxDao.dao.filter.NotEqualFilter;
import org.jxDao.dao.filter.NotILikeBetweenFilter;
import org.jxSwing.Cleanable;
import org.jxSwing.component.button.ClearButton;
import org.jxSwing.component.button.DeleteButton;
import org.jxSwing.component.button.SearchButton;
import org.jxSwing.component.comboBox.ComboBox;
import org.jxSwing.component.comboBox.YesNoComboBox;
import org.jxSwing.component.dialog.Dialog;
import org.jxSwing.component.dialog.ExceptionDialog;
import org.jxSwing.component.label.TimerLabel;
import org.jxSwing.component.panel.BeanTablePanel;
import org.jxSwing.component.panel.CardPanel;
import org.jxSwing.component.table.BeanTableColumn;
import org.jxSwing.component.text.CEPField;
import org.jxSwing.component.text.CNPJField;
import org.jxSwing.component.text.CPFField;
import org.jxSwing.component.text.DateField;
import org.jxSwing.component.text.EmailField;
import org.jxSwing.component.text.HourField;
import org.jxSwing.component.text.IEField;
import org.jxSwing.component.text.IPv4Field;
import org.jxSwing.component.text.NIREField;
import org.jxSwing.component.text.NumberField;
import org.jxSwing.component.text.PhoneField;
import org.jxSwing.component.text.RGField;
import org.jxSwing.component.text.TextField;
import org.jxSwing.i18n.I18N;
import org.jxUtils.Patterns;
import org.jxUtils.TypeUtil;
import org.jxUtils.check.CheckUtil;
import org.jxUtils.check.ParameterCheck;
import org.jxUtils.date.DateUtil;
import org.jxUtils.date.TimeFormatter;
import org.jxUtils.operator.IsNotNullOperator;
import org.jxUtils.operator.IsNullOperator;
import org.jxUtils.operator.Operator;

/**
 * @author Denilson Edinaldo Pinto
 * @porject jxController
 * @package org.jxController.view
 * @date 27/08/2014 - 18:34:58
 * @param <Bean>
 * @param <TDao>
 */
public class FilterDialog<Bean extends Persistable, TDao extends Dao<Bean>> extends Dialog {
	
	private static final long serialVersionUID = -258876407422207842L;
	
	private static final String FIRST_OR_LAST = "FIRST OR LAST";
	
	private TDao dao = null;
	
	private Filterable oldFilter = null;
	
	private Filterable filter = null;
	
	private Class<?>[] fetch = null;
	
	private JTabbedPane tbpnFilterRecords = null;
	
	private JPanel pnlFilter = null;
	
	private JPanel pnlField = null;
	
	private JLabel lblField = null;
	
	private JLabel lblOperator = null;
	
	private JLabel lblValue = null;
	
	private JLabel lblConcatenation = null;
	
	private JLabel lblAction = null;
	
	private ComboBox<BeanTableColumn> cbxField = null;
	
	private OperatorComboBox cbxOperator = null;
	
	private JPanel pnlConcatenation = null;
	
	private CardPanel pnlValue = null;
	
	private JButton btnAND = null;
	
	private JButton btnOR = null;
	
	private JPanel pnlAction = null;
	
	private DeleteButton btnCancel = null;
	
	private ClearButton btnClear = null;
	
	private SearchButton btnSearch = null;
	
	private JPanel pnlFilters = null;
	
	private BeanTablePanel<SearchFilter> tblFilters = null;
	
	private JPanel pnlRecords = null;
	
	private BeanTablePanel<Bean> tblRecords = null;
	
	private JPanel pnlStatus = null;
	
	private JProgressBar progressBar = null;
	
	private TimerLabel lblTimer = null;
	
	/**
	 *
	 */
	@SuppressWarnings("unused")
	private FilterDialog() {
	
		this(null, "", null);
	}
	
	/**
	 * @param owner
	 * @param title
	 * @param dao
	 * @param fetch
	 */
	public FilterDialog(final Frame owner, final String title, final TDao dao, final Class<?>... fetch) {
	
		this(owner, title, dao, null, fetch);
	}
	
	/**
	 * @param owner
	 * @param title
	 * @param dao
	 * @param filter
	 * @param fetch
	 */
	public FilterDialog(final Frame owner, final String title, final TDao dao, final Filterable filter, final Class<?>... fetch) {
	
		super(owner, title, true);
		
		this.dao = dao;
		this.filter = filter;
		this.fetch = fetch;
		this.oldFilter = filter != null ? filter.clone() : null;
		
		initGUI();
	}
	
	/**
	 * @return the Dao
	 */
	public final Dao<Bean> getDao() {
	
		return dao;
	}
	
	/**
	 * @return the Bean Class
	 */
	public final Class<?> getBeanClass() {
	
		return getDao().getBeanClass();
	}
	
	/**
	 * @param concatenation
	 */
	private void addFilterInTblFilters(final String concatenation) {
	
		final SearchFilter newFilter = new SearchFilter(getCbxField().getSelectedItem().toString(), getCbxOperator().getSelectedItem().toString(), getFormattedValue(), concatenation);
		
		getTblFilters().addRow(newFilter);
		
		clearFields(false);
		
		getCbxField().requestFocusInWindow();
	}
	
	/**
	 * @return Formatted Value
	 */
	private String getFormattedValue() {
	
		Object value = getValue();
		
		//
		if(value != null) {
			
			//
			if(TypeUtil.isDateOrTime(value)) {
				
				//
				if(value instanceof java.sql.Time) {
					
					return DateUtil.parse((java.sql.Time)value, "HH:mm:ss");
					
				}else if(value instanceof java.sql.Timestamp) {
					
					return DateUtil.parse((java.sql.Timestamp)value, "HH:mm:ss.SSS");
					
				}else if(value instanceof java.sql.Date) {
					
					return DateUtil.parse((java.sql.Date)value, "yyyy-MM-dd");
					
				}else if(value instanceof Date) {
					
					return DateUtil.parse((Date)value, "yyyy-MM-dd HH:mm:ss.SSS");
					
				}else {
					
					return DateUtil.parse(((Calendar)value).getTime(), "yyyy-MM-dd HH:mm:ss.SSS");
				}
				
			}else {
				
				return value.toString();
			}
		}
		
		return null;
	}
	
	/**
	 *
	 */
	private void applyFilter() {
	
		enabledFields(false);
		
		getBtnSearch().setVisible(false);
		getProgressBar().setString("");
		
		//
		new Thread(new Runnable() {
			
			@Override
			public void run() {
			
				getProgressBar().setIndeterminate(true);
				
				getLblTimer().start();
				
				try {
					//
					List<Bean> beans = null;
					
					Operator operator = getCbxOperator().getSelectedItem();
					
					//
					if(operator != null) {
						
						NumberField field = (NumberField)getPnlValue().getComponent(FIRST_OR_LAST);
						
						//
						if(operator.getSymbol().equals("FIRST")) {
							
							beans = getDao().getFirst(field.getIntValue(), filter, fetch);
							
						}else if(operator.getSymbol().equals("LAST")) {
							
							beans = getDao().getLast(field.getIntValue(), filter, fetch);
						}
						
					}else {
						
						beans = getDao().getByFilter(filter, fetch);
					}
					
					//
					getTblRecords().setRows(beans);
					getTblRecords().setAutoResizeColumns(true);
					
					getBtnClear().setVisible(true);
					
					getLblTimer().stop();
					getProgressBar().setIndeterminate(false);
					
					// Se não foram encontrados Registros na Base de Dados
					if( !getDao().isOperationWasAborted() && getTblRecords().isEmpty()) {
						
						JOptionPane.showMessageDialog(FilterDialog.this, org.jxController.i18n.I18N.controller().noRecordsWereFound());
						
					}else {
						
						// 
						if(filter != null && filter.getOrderBy() == null) {
							
							getTblRecords().sorter(0);
						}
						
						// Muda o foco para a aba de Resultado
						getTbpnFilterResult().setSelectedIndex(1);
					}
					
				}catch(final SQLException sqlException) {
					
					getLblTimer().stop();
					getProgressBar().setIndeterminate(false);
					
					ExceptionDialog.showMessage(sqlException);
					
				}catch(final NullPointerException nullPointerException) {
					// ignore
				}
				
				getBtnCancel().setVisible(false);
				getBtnClear().setVisible(true);
				getBtnSearch().setVisible(false);
				
				getLblTimer().stop();
				getProgressBar().setIndeterminate(false);
			}
		}).start();
	}
	
	/**
	 *
	 */
	private void clearComponentValue() {
	
		((NumberField)getPnlValue().getComponent(FIRST_OR_LAST)).clear();
		
		//
		for(final Component component : getPnlValue().getComponents()) {
			
			//
			if(component != null && component instanceof Cleanable) {
				
				((Cleanable)component).clear();
			}
		}
	}
	
	/**
	 * @param clearTblFilters
	 */
	private void clearFields(final boolean clearTblFilters) {
	
		showComponentValue(true);
		clearComponentValue();

		getCbxField().setEnabled(true);
		getCbxField().setSelectedItem(null);
		
		getCbxOperator().setEnabled(true);
		getCbxOperator().showFieldOperators(false);
		getCbxOperator().setSelectedItem(null);
		
		getBtnAND().setEnabled(false);
		getBtnOR().setEnabled(false);
		getBtnCancel().setVisible(false);
		getBtnClear().setVisible( !clearTblFilters);
		getBtnSearch().setVisible(true);
		
		getTblRecords().clear();
		
		getProgressBar().setString("");
		
		getLblTimer().reset();
		
		((Cleanable)getPnlValue().getComponent(FIRST_OR_LAST)).clear();
		
		getPnlValue().show("NULL");
		
		//
		if(clearTblFilters) {
			
			filter = oldFilter != null ? oldFilter.clone() : null;
			
			getTblFilters().clear();
		}
	}
	
	/**
	 *
	 */
	private void fillCbxField() {
	
		getCbxField().addItem(null);
		
		getPnlValue().add("NULL", new JLabel());
		
		// Campo para a busca de "FIRST" ou "LAST"
		NumberField field = new NumberField();
		field.setName(FIRST_OR_LAST);
		getPnlValue().add(FIRST_OR_LAST, field);
		
		//
		for(final BeanTableColumn column : getTblRecords().getColumns()) {
			
			//
			if(TypeUtil.isPrimitive(column.getType())) {
				
				getCbxField().addItem(column);
				
				getPnlValue().add(column.toString(), makeComponentValue(column));
			}
		}
	}
	
	/**
	 * @return value in {@link String}
	 */
	private String getStringValue() {
		
		Object value = getValue();
		
		return value != null ? value.toString() : "";
	}
	
	/**
	 * @return {@link Time} or {@link Date} or {@link String} or {@link Object} or null
	 */
	private Object getValue() {
	
		Object value = null;
		
		//
		if(getCbxField().getSelectedIndex() > -1) {
			
			BeanTableColumn column = getCbxField().getSelectedItem();
			
			//
			if(column != null) {
				
				final Component component = getPnlValue().getComponent(column.toString());
				
				//
				if(component != null) {
					
					//
					if(component instanceof HourField) {
						
						value = ((HourField)component).getTime();
						
					}else if(component instanceof DateField) {
						
						value = ((DateField)component).getDate();
						
					}else if(component instanceof NumberField) {
						
						value = ((NumberField)component).getValue();
						
						// 
						if(Patterns.isDouble(value.toString())) {
							
							value = ((NumberField)component).getDoubleValue();
							
						}else {
							
							value = ((NumberField)component).getIntValue();
						}
						
					}else if(component instanceof JComboBox) {
						
						value = ((JComboBox<?>)component).getSelectedItem();
						
					}else if(component instanceof JTextComponent) {
						
						value = ((JTextComponent)component).getText();
					}
				}
			}
		}
		
		return value;
	}
	
	/**
	 *
	 */
	private void initGUI() {
	
		getContentPane().add(getTbpnFilterResult(), BorderLayout.CENTER);
		getContentPane().add(getPnlStatus(), BorderLayout.SOUTH);
		
		fillCbxField();
		
		setSize(700, 400);
		setResizable(false);
		showExitQuestion(false);
	}
	
	/**
	 * @param field
	 */
	private Component makeComponentValue(final BeanTableColumn field) {
	
		Component component = null;
		
		final String fieldAlias = field.getAlias().toUpperCase().replaceAll("^[0-9]", "");
		
		//
		if(field.getType() == null) {
			
			component = new JLabel();
			
		}else if(TypeUtil.isTime(field.getType())) {
			
			TimeFormatter hourFormatter;
			
			//
			if(Time.class.isAssignableFrom(field.getType())) {
				
				//
				if(Locale.getDefault().getCountry().equalsIgnoreCase("BR")) {
					
					hourFormatter = new TimeFormatter("HH:mm:ss", "##:##:##");
					
				}else {
					
					hourFormatter = new TimeFormatter("hh:mm:ss a", "##:##:## UU");
				}
				
			}else {
				
				//
				if(Locale.getDefault().getCountry().equalsIgnoreCase("BR")) {
					
					hourFormatter = new TimeFormatter("HH:mm:ss.SSS", "##:##:##.###");
					
				}else {
					
					hourFormatter = new TimeFormatter("hh:mm:ss.SSS a", "##:##:##.### UU");
				}
			}
			
			component = new HourField(hourFormatter.getMask());
			
		}else if(TypeUtil.isDate(field.getType())) {
			
			//
			if(java.sql.Date.class.isAssignableFrom(field.getType())) {
				
				component = new DateField();
				
			}else {
				
				TimeFormatter dateFormatter;
				
				//
				if(Locale.getDefault().getCountry().equalsIgnoreCase("BR")) {
					
					dateFormatter = new TimeFormatter("dd/MM/yyyy HH:mm:ss.SSS", "##/##/#### ##:##:##.###");
					
				}else {
					
					dateFormatter = new TimeFormatter("yyyy-MM-dd hh:mm:ss.SSS a", "####-##-## ##:##:##.### UU");
				}
				
				component = new DateField(dateFormatter, false);
			}
			
		}else if(TypeUtil.isNumber(field.getType())) {
			
			final boolean acceptDecimal = TypeUtil.isRealNumber(field.getType());
			
			component = new NumberField(acceptDecimal);
			
		}else if(Boolean.class.isAssignableFrom(field.getType())) {
			
			component = new YesNoComboBox();
			
		}else if(fieldAlias.contains(I18N.component().cpf())) {
			
			component = new CPFField();
			
		}else if(fieldAlias.contains(I18N.component().rg())) {
			
			component = new RGField();
			
		}else if(fieldAlias.contains(I18N.component().cnpj())) {
			
			component = new CNPJField();
			
		}else if(fieldAlias.contains(I18N.component().phone().toUpperCase())) {
			
			component = new PhoneField();
			
		}else if(fieldAlias.contains(I18N.component().email())) {
			
			component = new EmailField();
			
		}else if(fieldAlias.contains(I18N.component().cep())) {
			
			component = new CEPField();
			
		}else if(fieldAlias.contains(I18N.component().hour())) {
			
			component = new HourField();
			
		}else if(fieldAlias.contains(I18N.component().ie())) {
			
			component = new IEField();
			
		}else if(fieldAlias.contains(I18N.component().ipv4())) {
			
			component = new IPv4Field();
			
		}else if(fieldAlias.contains(I18N.component().nire())) {
			
			component = new NIREField();
			
		}else {
			
			component = new TextField();
		}
		
		//
		component.setName(field.toString());
		component.setVisible(false);
		
		//
		component.addKeyListener(new KeyAdapter() {
			
			@Override
			public void keyPressed(final KeyEvent keyEvent) {
			
				//
				if(keyEvent.getKeyCode() == KeyEvent.VK_ENTER) {
					
					makeFilter(getBtnAND().getText());
				}
			}
		});
		
		return component;
	}
	
	/**
	 * @param concatenation
	 */
	private void makeFilter(final String concatenation) {
	
		ParameterCheck.isNullOrAbsoluteEmpty(concatenation, "concatenation");
		
		Filterable newFilter = null;
		
		//
		if(getCbxField().getSelectedItem() != null) {
			
			//
			if(getCbxOperator().getSelectedItem() != null) {
				
				final String columnName = getDao().getColumnByField(getCbxField().getSelectedItem().getName());
				
				//
				if(columnName != null) {
					
					//
					switch(getCbxOperator().getSelectedItem().getSymbol()) {
					
						case "=":
							
							newFilter = new EqualFilter(columnName, getValue());
							
							break;
						
						case "!=":
							
							newFilter = new NotEqualFilter(columnName, getValue());
							
							break;
						
						case ">=":
							
							newFilter = new GreaterOrEqualFilter(columnName, getValue());
							
							break;
						
						case ">":
							
							newFilter = new GreaterThanFilter(columnName, getValue());
							
							break;
						
						case "<=":
							
							newFilter = new LessOrEqualFilter(columnName, getValue());
							
							break;
						
						case "<":
							
							newFilter = new LessThanFilter(columnName, getValue());
							
							break;
						
						case "IS NULL":
							
							newFilter = new IsNullFilter(columnName);
							
							break;
						
						case "IS NOT NULL":
							
							newFilter = new IsNotNullFilter(columnName);
							
							break;
						
						case "ILIKE BETWEEN":
							
							newFilter = new ILikeBetweenFilter(columnName, getStringValue());
							
							break;
							
						case "NOT ILIKE BETWEEN":
							
							newFilter = new NotILikeBetweenFilter(columnName, getStringValue());
							
							break;
						
						default:
							
							break;
					}
					
				}else {
					
					JOptionPane.showMessageDialog(null, org.jxController.i18n.I18N.controller().noCorrespondingColumnWasFoundToFieldInDatabase(getCbxField().getSelectedItem()));
					
					enabledFields(true);
					
					getProgressBar().setIndeterminate(false);
					
					getCbxField().requestFocusInWindow();
				}
				
			}else {
				
				JOptionPane.showMessageDialog(null, I18N.component().selectOperator());
				
				enabledFields(true);
				
				getProgressBar().setIndeterminate(false);
				
				getCbxOperator().requestFocusInWindow();
			}
		}
		
		//
		if(newFilter != null) {
			
			//
			if(filter == null) {
				
				filter = newFilter;
				
			}else {
				
				// 
				if(CheckUtil.isNullOrAbsoluteEmpty(filter.getColumnName()) && CheckUtil.isNullOrEmpty(filter.getValue())) {
					
					newFilter.orderBy(filter.getOrderBy());
					newFilter.groupBy(filter.getGroupBy());
					
					filter = newFilter;
					
				}else {
					
					//
					if(getTblFilters().isEmpty() || getTblFilters().getLastRow().getConcatenation().equalsIgnoreCase(getBtnAND().getText())) {
						
						filter.and(newFilter);
						
					}else {
						
						filter.or(newFilter);
					}
				}
			}
			
			addFilterInTblFilters(concatenation);
		}
	}
	
	/**
	 *
	 */
	private void requestFocusComponentValue() {
	
		//
		if(getCbxField().getSelectedIndex() > -1) {
			
			BeanTableColumn column = getCbxField().getSelectedItem();
			
			//
			if(column != null) {
				
				final Component component = getPnlValue().getComponent(column.toString());
				
				//
				if(component != null) {
					
					component.requestFocusInWindow();
				}
			}
		}
	}
	
	/**
	 * @param enabled
	 * @return the {@link Component} or null
	 */
	private Component showComponentValue(final boolean enabled) {
	
		BeanTableColumn column = getCbxField().getSelectedItem();
		
		//
		if(column != null) {
			
			final Component component = getPnlValue().getComponent(column.toString());
			
			//
			if(component != null) {
				
				getPnlValue().show(column.toString());
				
				component.setEnabled(enabled);
				component.setSize(getPnlValue().getWidth(), getPnlValue().getHeight());
			}
			
			clearComponentValue();
			
			return component;
		}
		
		return null;
	}
	
	/**
	 * @param enabled
	 */
	protected void enabledFields(final boolean enabled) {
	
		//
		if( !getPnlValue().getComponent(FIRST_OR_LAST).isVisible()) {
			
			getCbxOperator().showFieldOperators(true);
			getCbxOperator().setEnabled(enabled);
			
			getPnlValue().show(FIRST_OR_LAST);
			getPnlValue().getComponent(FIRST_OR_LAST).setEnabled(enabled);
			
		}else {
			
			getBtnCancel().setVisible( !enabled);
			getCbxField().setEnabled(enabled);
			getBtnClear().setVisible(enabled);
			getTblFilters().setEnabled(enabled);
			getTblRecords().setEnabled(enabled);
			getBtnAND().setEnabled(enabled);
			getBtnOR().setEnabled(enabled);
			
			//
			if(getCbxField().getSelectedIndex() > -1) {
				
				BeanTableColumn column = getCbxField().getSelectedItem();
				
				//
				if(column != null) {
					
					final Component component = getPnlValue().getComponent(column.toString());
					
					//
					if(component != null) {
						
						component.setEnabled(enabled);
						
						showComponentValue(enabled);
					}
				}
			}
		}
	}
	
	public JButton getBtnAND() {
	
		if(btnAND == null) {
			btnAND = new JButton(org.jxUtils.i18n.I18N.operator().and());
			btnAND.setEnabled(false);
			btnAND.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(final ActionEvent actionEvent) {
				
					makeFilter(getBtnAND().getText());
				}
			});
		}
		return btnAND;
	}
	
	public DeleteButton getBtnCancel() {
	
		if(btnCancel == null) {
			btnCancel = new DeleteButton();
			btnCancel.setVisible(false);
			btnCancel.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(final ActionEvent actionEvent) {
				
					getDao().abortOperation();
					
					getLblTimer().stop();
					
					getTbpnFilterResult().setSelectedIndex(0);
				}
			});
		}
		return btnCancel;
	}
	
	public ClearButton getBtnClear() {
	
		if(btnClear == null) {
			btnClear = new ClearButton();
			btnClear.setVisible(false);
			btnClear.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(final ActionEvent actionEvent) {
				
					clearFields(true);
					
					getCbxField().requestFocusInWindow();
				}
			});
		}
		return btnClear;
	}
	
	public JButton getBtnOR() {
	
		if(btnOR == null) {
			btnOR = new JButton(org.jxUtils.i18n.I18N.operator().or());
			btnOR.setEnabled(false);
			btnOR.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(final ActionEvent actionEvent) {
				
					makeFilter(getBtnOR().getText());
				}
			});
		}
		return btnOR;
	}
	
	public SearchButton getBtnSearch() {
	
		if(btnSearch == null) {
			btnSearch = new SearchButton();
			btnSearch.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(final ActionEvent actionEvent) {
				
					applyFilter();
				}
			});
		}
		return btnSearch;
	}
	
	public ComboBox<BeanTableColumn> getCbxField() {
	
		if(cbxField == null) {
			cbxField = new ComboBox<>();
			cbxField.addItemListener(new ItemListener() {
				
				@Override
				public void itemStateChanged(final ItemEvent itemEvent) {
					
					final Component component = showComponentValue(getCbxField().getSelectedItem() != null);
					
					//
					if(component != null) {
						
						component.setEnabled(getCbxOperator().getSelectedItem() != null);
						
						getCbxOperator().showFieldOperators(true);
						getCbxOperator().setEnabled(true);
						getCbxOperator().requestFocusInWindow();
						
					}else {
						
						getCbxOperator().setSelectedItem(null);
						
						getCbxOperator().showFieldOperators(false);
						getBtnAND().setEnabled(false);
						getBtnOR().setEnabled(false);
						
						getCbxField().requestFocusInWindow();
					}
				}
			});
		}
		return cbxField;
	}
	
	public OperatorComboBox getCbxOperator() {
	
		if(cbxOperator == null) {
			cbxOperator = new OperatorComboBox();
			cbxOperator.addItemListener(new ItemListener() {
				
				@Override
				public void itemStateChanged(final ItemEvent itemEvent) {
					
					Component component = showComponentValue(itemEvent.getStateChange() == ItemEvent.SELECTED);
					
					final Object operator = getCbxOperator().getSelectedItem();
					
					//
					if(component != null && operator != null) {
						
						getBtnAND().setEnabled(true);
						getBtnOR().setEnabled(true);
						
						//
						if(operator instanceof IsNullOperator || operator instanceof IsNotNullOperator) {
							
							component.setVisible(false);
							
							getBtnAND().requestFocusInWindow();
							
							return;
						}
						
						requestFocusComponentValue();
						
					}else if(operator != null) {
						
						getBtnAND().setEnabled(false);
						getBtnOR().setEnabled(false);
						
						getPnlValue().show(FIRST_OR_LAST);
						
						component = getPnlValue().getComponent(FIRST_OR_LAST);
						
						component.setSize(getPnlValue().getWidth(), getPnlValue().getHeight());
						
						((NumberField)component).setValue(1);
						component.requestFocusInWindow();
						
						component.setEnabled( !(operator instanceof FirstOneOperator || operator instanceof LastOneOperator));
						
					}else {
						
						getBtnAND().setEnabled(false);
						getBtnOR().setEnabled(false);
						
						getCbxOperator().requestFocusInWindow();
					}
				}
			});
		}
		return cbxOperator;
	}
	
	public JLabel getLblAction() {
	
		if(lblAction == null) {
			lblAction = new JLabel();
		}
		return lblAction;
	}
	
	public JLabel getLblConcatenation() {
	
		if(lblConcatenation == null) {
			lblConcatenation = new JLabel(I18N.component().concatenation() + ":");
		}
		return lblConcatenation;
	}
	
	public JLabel getLblField() {
	
		if(lblField == null) {
			lblField = new JLabel(I18N.component().field() + ":");
		}
		return lblField;
	}
	
	public JLabel getLblOperator() {
	
		if(lblOperator == null) {
			lblOperator = new JLabel(org.jxUtils.i18n.I18N.operator().operator() + ":");
		}
		return lblOperator;
	}
	
	public TimerLabel getLblTimer() {
	
		if(lblTimer == null) {
			lblTimer = new TimerLabel();
		}
		return lblTimer;
	}
	
	public JLabel getLblValue() {
	
		if(lblValue == null) {
			lblValue = new JLabel(I18N.component().value() + ":");
		}
		return lblValue;
	}
	
	public JPanel getPnlAction() {
	
		if(pnlAction == null) {
			pnlAction = new JPanel();
			pnlAction.setLayout(new GridLayout(0, 3, 5, 5));
			pnlAction.add(getBtnCancel());
			pnlAction.add(getBtnClear());
			pnlAction.add(getBtnSearch());
		}
		return pnlAction;
	}
	
	public JPanel getPnlConcatenation() {
	
		if(pnlConcatenation == null) {
			pnlConcatenation = new JPanel();
			pnlConcatenation.setLayout(new GridLayout(0, 2, 5, 5));
			pnlConcatenation.add(getBtnAND());
			pnlConcatenation.add(getBtnOR());
		}
		return pnlConcatenation;
	}
	
	public JPanel getPnlField() {
	
		if(pnlField == null) {
			pnlField = new JPanel();
			pnlField.setLayout(new GridLayout(0, 5, 5, 5));
			pnlField.add(getLblField());
			pnlField.add(getLblOperator());
			pnlField.add(getLblValue());
			pnlField.add(getLblConcatenation());
			pnlField.add(getLblAction());
			pnlField.add(getCbxField());
			pnlField.add(getCbxOperator());
			pnlField.add(getPnlValue());
			pnlField.add(getPnlConcatenation());
			pnlField.add(getPnlAction());
		}
		return pnlField;
	}
	
	public JPanel getPnlFilter() {
	
		if(pnlFilter == null) {
			pnlFilter = new JPanel();
			pnlFilter.setLayout(new BorderLayout());
			pnlFilter.add(getPnlField(), BorderLayout.NORTH);
			pnlFilter.add(getPnlFilters(), BorderLayout.CENTER);
		}
		return pnlFilter;
	}
	
	public JPanel getPnlFilters() {
	
		if(pnlFilters == null) {
			pnlFilters = new JPanel(new BorderLayout());
			pnlFilters.setBorder(new TitledBorder(new LineBorder(new Color(184, 207, 229)), I18N.component().filters() + ":", TitledBorder.LEADING, TitledBorder.BELOW_TOP, null, null));
			pnlFilters.add(getTblFilters(), BorderLayout.CENTER);
		}
		return pnlFilters;
	}
	
	public JPanel getPnlRecords() {
	
		if(pnlRecords == null) {
			pnlRecords = new JPanel();
			pnlRecords.setLayout(new BorderLayout(0, 0));
			pnlRecords.add(getTblRecords(), BorderLayout.CENTER);
		}
		return pnlRecords;
	}
	
	public JPanel getPnlStatus() {
	
		if(pnlStatus == null) {
			pnlStatus = new JPanel();
			
			final GridBagLayout gblPnlStatus = new GridBagLayout();
			gblPnlStatus.columnWidths = new int[]{0, 0, 0};
			gblPnlStatus.rowHeights = new int[]{25, 0};
			gblPnlStatus.columnWeights = new double[]{1.0, 0.0, Double.MIN_VALUE};
			gblPnlStatus.rowWeights = new double[]{0.0, Double.MIN_VALUE};
			
			pnlStatus.setLayout(gblPnlStatus);
			
			final GridBagConstraints gbcProgressBar = new GridBagConstraints();
			gbcProgressBar.fill = GridBagConstraints.HORIZONTAL;
			gbcProgressBar.insets = new Insets(0, 10, 0, 10);
			gbcProgressBar.gridx = 0;
			gbcProgressBar.gridy = 0;
			
			pnlStatus.add(getProgressBar(), gbcProgressBar);
			
			final GridBagConstraints gbcLblTimer = new GridBagConstraints();
			gbcLblTimer.insets = new Insets(5, 10, 5, 10);
			gbcLblTimer.fill = GridBagConstraints.VERTICAL;
			gbcLblTimer.gridx = 1;
			gbcLblTimer.gridy = 0;
			
			pnlStatus.add(getLblTimer(), gbcLblTimer);
		}
		return pnlStatus;
	}
	
	public CardPanel getPnlValue() {
	
		if(pnlValue == null) {
			pnlValue = new CardPanel();
		}
		return pnlValue;
	}
	
	public JProgressBar getProgressBar() {
	
		if(progressBar == null) {
			progressBar = new JProgressBar();
		}
		return progressBar;
	}
	
	public BeanTablePanel<SearchFilter> getTblFilters() {
	
		if(tblFilters == null) {
			tblFilters = new BeanTablePanel<>(SearchFilter.class);
			tblFilters.setVisibleCrudButtons(false, false, true);
			tblFilters.setVisibleExport(false);
			tblFilters.setVisibleFilter(false);
			tblFilters.setVisiblePrint(false);
			tblFilters.locked();
			//
			tblFilters.setColumnAlias(0, I18N.component().field());
			tblFilters.setColumnAlias(1, org.jxUtils.i18n.I18N.operator().operator());
			tblFilters.setColumnAlias(2, I18N.component().value());
			tblFilters.setColumnAlias(3, I18N.component().concatenation());
		}
		return tblFilters;
	}
	
	public BeanTablePanel<Bean> getTblRecords() {
	
		if(tblRecords == null) {
			tblRecords = new BeanTablePanel<>(getBeanClass());
			tblRecords.setVisibleCrudButtons(false, false, false);
			tblRecords.setAutoResizeColumns(true);
			tblRecords.locked();
		}
		return tblRecords;
	}
	
	public JTabbedPane getTbpnFilterResult() {
	
		if(tbpnFilterRecords == null) {
			tbpnFilterRecords = new JTabbedPane(SwingConstants.TOP);
			tbpnFilterRecords.addTab(I18N.component().filters(), null, getPnlFilter(), null);
			tbpnFilterRecords.addTab(I18N.component().records(), null, getPnlRecords(), null);
		}
		return tbpnFilterRecords;
	}
	
	/**
	 * @author Denilson Edinaldo Pinto
	 * @porject jxController
	 * @package org.jxController.view
	 * @date 01/09/2014 - 07:38:51
	 */
	public final class SearchFilter {
		
		private String field;
		
		private String operator;
		
		private String value;
		
		private String concatenation;
		
		/**
		 * @param field
		 * @param operator
		 * @param value
		 * @param concatenation
		 */
		public SearchFilter(final String field, final String operator, final String value, final String concatenation) {
		
			this.field = field;
			this.operator = operator;
			this.value = value;
			this.concatenation = concatenation;
		}
		
		private FilterDialog<?, ?> getOuterType() {
		
			return FilterDialog.this;
		}
		
		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#equals(java.lang.Object)
		 */
		@Override
		public boolean equals(final Object obj) {
		
			if(this == obj) {
				return true;
			}
			if(obj == null) {
				return false;
			}
			if( !(obj instanceof FilterDialog.SearchFilter)) {
				return false;
			}
			@SuppressWarnings("unchecked")
			final SearchFilter other = (SearchFilter)obj;
			if( !getOuterType().equals(other.getOuterType())) {
				return false;
			}
			if(concatenation == null) {
				if(other.concatenation != null) {
					return false;
				}
			}else if( !concatenation.equals(other.concatenation)) {
				return false;
			}
			if(field == null) {
				if(other.field != null) {
					return false;
				}
			}else if( !field.equals(other.field)) {
				return false;
			}
			if(operator == null) {
				if(other.operator != null) {
					return false;
				}
			}else if( !operator.equals(other.operator)) {
				return false;
			}
			if(value == null) {
				if(other.value != null) {
					return false;
				}
			}else if( !value.equals(other.value)) {
				return false;
			}
			return true;
		}
		
		/**
		 * @return the concatenation
		 */
		public String getConcatenation() {
		
			return concatenation;
		}
		
		/**
		 * @return the field
		 */
		public String getField() {
		
			return field;
		}
		
		/**
		 * @return the operator
		 */
		public String getOperator() {
		
			return operator;
		}
		
		/**
		 * @return the value
		 */
		public String getValue() {
		
			return value;
		}
		
		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#hashCode()
		 */
		@Override
		public int hashCode() {
		
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + (concatenation == null ? 0 : concatenation.hashCode());
			result = prime * result + (field == null ? 0 : field.hashCode());
			result = prime * result + (operator == null ? 0 : operator.hashCode());
			result = prime * result + (value == null ? 0 : value.hashCode());
			return result;
		}
		
		/**
		 * @param concatenation
		 *        the concatenation to set
		 */
		public void setConcatenation(final String concatenation) {
		
			this.concatenation = concatenation;
		}
		
		/**
		 * @param field
		 *        the field to set
		 */
		public void setField(final String field) {
		
			this.field = field;
		}
		
		/**
		 * @param operator
		 *        the operator to set
		 */
		public void setOperator(final String operator) {
		
			this.operator = operator;
		}
		
		/**
		 * @param value
		 *        the value to set
		 */
		public void setValue(final String value) {
		
			this.value = value;
		}
		
		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
		
			return (field != null ? "field=" + field + ", " : "") + (operator != null ? "operator=" + operator + ", " : "") + (value != null ? "value=" + value + ", " : "") + (concatenation != null ? "concatenation=" + concatenation : "");
		}
	}
}
